#include "file_op.h"
#include "common.h"

namespace project
{
    namespace largefile
    {
        FileOperation::FileOperation(const std::string &file_name, const int open_flags) : fd_(-1), open_flags_(open_flags)
        {
            file_name_ = strdup(file_name.c_str());
        }

        FileOperation::~FileOperation()
        {
            if (fd_ > 0)
            {
                ::close(fd_); // 调用全局作用域里的close函数
            }
            if (NULL != file_name_)
            {
                free(file_name_);
                file_name_ = NULL;
            }
        }

        int FileOperation::open_file()
        {
            if (fd_ > 0)
            {
                close(fd_);
                fd_ = -1;
            }

            fd_ = ::open(file_name_, open_flags_, OPEN_MODE);

            if (fd_ < 0)
            {
                return -errno;
            }
            return fd_;
        }

        void FileOperation::close_file()
        {
            if (fd_ < 0)
            {
                return;
            }

            ::close(fd_);
            fd_ = -1;
        }

        int FileOperation::pread_file(char *buf, const int32_t nbytes, const int64_t offset)
        {
            // 分多次读取
            int32_t left = nbytes;        // 剩余的还未读的字节数
            int64_t read_offset = offset; // 读的偏移
            int32_t read_len = 0;         // 已经读取的长度
            char *p_tmp = buf;            // 指针

            int i = 0; // 读取的次数

            while (left > 0)
            {
                ++i; // 读取次数加1

                if (i >= MAX_DISK_TIMES) break;

                if (check_file() < 0) return -errno; // 确保文件打开

                read_len = ::pread64(fd_, p_tmp, left, read_offset);

                if (read_len < 0)
                {
                    read_len = -errno;

                    if (-read_len == EINTR || EAGAIN == -read_len) // 系统调用被中断信号打断或者在非阻塞io的情况下，表示暂时没有可用资源
                    {
                        continue;
                    }
                    else if (EBADF == -read_len)
                    {
                        fd_ = -1;
                        continue;
                    }
                    else
                    {
                        return read_len;
                    }
                }
                else if (0 == read_len)
                {
                    break;
                }

                left -= read_len;
                p_tmp += read_len;
                read_offset += read_len;
            }

            // 并没有读完
            if (0 != left)
            {
                return EXIT_DISK_OPER_INCOMPLETE;
            }

            return TFS_SUCCESS;
        }

        int FileOperation::pwrite_file(const char *buf, const int32_t nbytes, const int64_t offset)
        {

            int32_t left = nbytes;
            int64_t write_offset = offset;
            int32_t written_len = 0;
            const char *p_tmp = buf;

            int i = 0;

            while (left > 0)
            {
                ++i;

                if (i >= MAX_DISK_TIMES)
                    break;

                if (check_file() < 0)
                    return -errno;

                written_len = ::pwrite64(fd_, p_tmp, left, write_offset);

                if (written_len < 0)
                {
                    written_len = -errno;

                    if (-written_len == EINTR || EAGAIN == -written_len)
                    {
                        continue;
                    }
                    else if (EBADF == -written_len)
                    {
                        fd_ = -1;
                        continue;
                    }
                    else
                    {
                        return written_len;
                    }
                }
                

                left -= written_len;
                p_tmp += written_len;
                write_offset += written_len;
            }

            if (0 != left)
            {
                return EXIT_DISK_OPER_INCOMPLETE;
            }

            return TFS_SUCCESS;
        }

        int FileOperation::write_file(const char *buf, const int32_t nbytes) 
        {

            int32_t left = nbytes;
            
            int32_t written_len = 0;
            const char *p_tmp = buf;

            int i = 0;

            while (left > 0)
            {
                ++i;

                if (i >= MAX_DISK_TIMES)
                    break;

                if (check_file() < 0)
                    return -errno;

                written_len = ::write(fd_, p_tmp, left);

                if (written_len < 0)
                {
                    written_len = -errno;

                    if (-written_len == EINTR || EAGAIN == -written_len)
                    {
                        continue;
                    }
                    else if (EBADF == -written_len)
                    {
                        fd_ = -1;
                        continue;
                    }
                    else
                    {
                        return written_len;
                    }
                }
                
                left -= written_len;
                p_tmp += written_len;

            }

            if (0 != left)
            {
                return EXIT_DISK_OPER_INCOMPLETE;
            }

            return TFS_SUCCESS;

        }

        int64_t FileOperation::get_file_size()
        {
            int fd = check_file();

            if (fd < 0)
            {
                return -1;
            }

            struct stat statbuf;
            if (fstat(fd, &statbuf) != 0)
            {
                return -1;
            }

            return statbuf.st_size;
        }

        int FileOperation::check_file()
        {
            if (fd_ < 0)
            {
                fd_ = open_file();
            }

            return fd_;
        }

        int FileOperation::ftruncate_file(const int64_t length)
        {
            int fd = check_file();

            if (fd < 0)
            {
                return fd;
            }

            return ftruncate(fd, length);
        }

        int FileOperation::seek_file(const int64_t offset)
        {
            int fd = check_file();

            if (fd < 0)
            {
                return fd;
            }

            return lseek(fd, offset, SEEK_SET);
        }

        int FileOperation::flush_file()
        {
            if (open_flags_ & O_SYNC)
            {
                return 0;
            } // 以同步的方式去打开文件就没必要flush了

            int fd = check_file();
            if (fd < 0)
            {
                return fd;
            }

            return fsync(fd);
        }

        int FileOperation::unlink_file()
        {
            close_file();
            return ::unlink(file_name_);
        }

    }
}